﻿using System;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Shapes;
using GBL.Repository.CoatOfArms;
using GBL.Repository.Resources;
using GBlason.Common;
using GBlason.Common.Attribute;
using GBlason.Common.Converter;
using GBlason.Common.Tools;
using GBlason.ViewModel.Contract;
using GBlason.ViewModel.General;
using GBlasonLogic.Tools;

namespace GBlason.ViewModel.CoatOfArm
{
    /// <summary>
    /// Handle the presentation logic for the root of a coat of arm
    /// </summary>
    public class CoatOfArmViewModel : CoatOfArmComponent, IDivisible
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="CoatOfArmViewModel"/> class.
        /// Initialize the shape with the first found resource shape
        /// Initialize the tree by setting this object as the root
        /// </summary>
        public CoatOfArmViewModel()
        {
            if (GbrFileViewModel.GetResources.ScaledForMenuShapeResources.Any())
                CurrentShape = GbrFileViewModel.GetResources.ScaledForMenuShapeResources[0];
            Parent = null; //this is the root of the coat of arms
            FlattenedTreeView = new OrderedDictionary(); //key = object, value = path for binding
            InitialiseFlatPath();
        }

        /// <summary>
        /// Initialises the flat path.
        /// </summary>
        private void InitialiseFlatPath()
        {
            var pathBack = new Path();
            var bindingPath = new Binding
            {
                Source = this,
                Path = new PropertyPath("CurrentShape.Geometry"),
                Mode = BindingMode.OneWay
            };
            pathBack.SetBinding(Path.DataProperty, bindingPath);
            pathBack.Fill = Application.Current.MainWindow.FindResource("FontDisabledBackgroundBrush") as Brush;

            FlattenedTreeView.Add(this, pathBack);

            var pathEffect = new Path();
            var bpathEffect = new Binding
            {
                Source = this,
                Path = new PropertyPath("CurrentShape.Geometry"),
                Mode = BindingMode.OneWay
            };
            pathEffect.SetBinding(Path.DataProperty, bpathEffect);
            pathEffect.Fill = Application.Current.MainWindow.FindResource("CustomMiniShapeGlossyBrush") as Brush;
            FlattenedTreeView.Add(SpecialCoAEffectViewModel.Instance, pathEffect);
        }

        public GbsFileViewModel ParentFile { get; set; }

        private CoatOfArms _originObject;

        /// <summary>
        /// Return the CoatOfArms from the repository linked with this object
        /// </summary>
        public override Object OriginObject
        {
            get { return _originObject ?? (_originObject = new CoatOfArms { Shape = CurrentShape.ConvertToShape() }); }
            set
            {
                if (value == _originObject) return;
                var coa = value as CoatOfArms;
                if (coa != null)
                    CurrentShape = coa.Shape.ConvertToViewModel();
                _originObject = coa;
                OnPropertyChanged("OriginObject");
            }
        }

        #region COA properties

        /// <summary>
        /// Gets or sets the current shape of the coat of armss.
        /// </summary>
        /// <value>
        /// The current shape.
        /// </value>
        [CoaProperty("PropertyNameShape")]
        public ShapeViewModel CurrentShape
        {
            get { return _currentShape; }
            set
            {
                if (_currentShape == value)
                    return;
                _currentShape = value;
                UpdateShapeFromParent();
                OnPropertyChanged("CurrentShape");
            }
        }


        private ShapeViewModel _currentShape;

        public override String Geometry
        {
            get { return CurrentShape.Geometry; }
            set { CurrentShape.Geometry = value; }
        }

        /// <summary>
        /// Start of the cascading update. Call the UpdateShapeFromParent on all the Children.
        /// </summary>
        /// <remarks>Do not update this instance Geometry as it will create an recursive infinite loop.
        /// If one of the IShapable to be updated is the son of a non IShapable Children we have a hole !</remarks>
        public override void UpdateShapeFromParent()
        {
            foreach (var child in Children)
                child.UpdateShapeFromParent();
        }

        #endregion

        #region ICommandTarget

        #endregion

        #region Viewer View Model (flat version)

        public OrderedDictionary FlattenedTreeView { get; private set; }

        /// <summary>
        /// Adds the child in dictionary.
        /// </summary>
        /// <param name="child">The child.</param>
        /// <param name="index">The index.</param>
        public virtual void AddChildInDictionary(CoatOfArmComponent child, int index)
        {
            var path = BindingManager.CreateAndBindPath(child);
            FlattenedTreeView.Insert(index, child, path);
            OnPropertyChanged("FlattenedTreeView");
        }

        /// <summary>
        /// Adds the child in dictionary.
        /// By default the child is added just before its parent. if no parent then it is added as the first element.
        /// If the parent doesn't exist it's added as the last element minus the effect which is always the last element.
        /// For every other behavior the method can be override
        /// </summary>
        /// <param name="child">The child.</param>
        /// <remarks>
        /// The path is automatically created in this method
        /// </remarks>
        public virtual void AddChildInDictionary(CoatOfArmComponent child)
        {
            if (FlattenedTreeView == null || child == null)
                return;
            try
            {
                var index = 0;
                if (child.Parent != null && FlattenedTreeView.Contains(child.Parent))
                    index = FlattenedTreeView.IndexOfKey(child.Parent);
                if (index == -1)
                    index = FlattenedTreeView.Count > 0 ? FlattenedTreeView.Count - 1 : 0;
                else
                    index++;

                AddChildInDictionary(child, index);
            }
            catch (IndexOutOfRangeException ex)
            {
                throw new CustomException("Erreur lors du calcul de l'index d'integration du path", ex);
            }
        }

        public void RemoveChildFromDictionary(CoatOfArmComponent child)
        {
            if (FlattenedTreeView.Contains(child))
                FlattenedTreeView.Remove(child);
        }

        #endregion


        #region IDivisible

        public void AddDivision(DivisionViewModel division)
        {
            AddChild(division);
        }

        public Visibility DivisibleVisibility
        {
            get { return Visibility.Visible; }
        }

        /// <summary>
        /// Gets the constraints. Always refreshed, the call can be expensive
        /// </summary>
        /// <returns></returns>
        public ConstraintDivisionSet GetDivisionsConstraints()
        {
            return new ConstraintDivisionSet
            {
                CurrentGeometry = Geometry,
                Type = typeof(CoatOfArmViewModel),
                TypeOfDivision = DivisionType.NoDivision
            };
        }

        public void RemoveDivision(DivisionViewModel component)
        {
            RemoveChild(component);
        }

        #endregion
    }
}
